package com.peng.mybatis.executor.resultset;

import cn.hutool.core.util.StrUtil;
import com.peng.mybatis.executor.Executor;
import com.peng.mybatis.io.Resources;
import com.peng.mybatis.mapping.BoundSql;
import com.peng.mybatis.mapping.MappedStatement;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Slf4j
public class DefaultResultSetHandler implements ResultSetHandler {

    private final BoundSql boundSql;

    public DefaultResultSetHandler(Executor executor, MappedStatement mappedStatement, BoundSql boundSql) {
        this.boundSql = boundSql;
    }

    @Override
    public <E> List<E> handleResultSet(Statement statement) throws SQLException {
        ResultSet resultSet = statement.getResultSet();
        try {
            return resultSet2Objects(resultSet, Resources.classForName(boundSql.getResultType()));
        } catch (Exception e) {
            log.error("解析执行结果失败：{}", e.getMessage(), e);
            return List.of();
        }
    }

    @SuppressWarnings("unchecked")
    private <E> List<E> resultSet2Objects(ResultSet resultSet, Class<?> resClass) {
        List<E> res = new ArrayList<>();
        try {
            ResultSetMetaData metaData = resultSet.getMetaData();
            int columnCount = metaData.getColumnCount();
            while (resultSet.next()) {
                E e = (E) resClass.getDeclaredConstructor().newInstance();
                for (int i = 1; i <= columnCount; i++) {
                    String columnName = metaData.getColumnName(i);
                    Object val = resultSet.getObject(i);
                    String setter = StrUtil.genSetter(columnName);
                    Method setterMethod = val instanceof Timestamp
                            ? resClass.getMethod(setter, Date.class) : resClass.getMethod(setter, val.getClass());
                    setterMethod.invoke(e, val);
                }
                res.add(e);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return res;
    }

}
